package com.yuncheng.sms.utils;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.yuncheng.system.orguser.entity.SysUser;
import com.yuncheng.system.orguser.service.ISysUserService;
import com.yuncheng.utils.RedisUtil;
import com.yuncheng.vo.HttpResult;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.util.WebUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
 * ForgetPasswordPatchAOP
 *
 * @date 2023/4/20
 */
@Aspect
@Component
public class ForgetPasswordPatchAOP {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final static String FORGET_PASSWORD_DATA_PREFIX = "forgetpasswordpage:";

    @Value("${forget_password_page_url:/index/alteration}")
    private String forgetPasswordPageUrl;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private ISysUserService sysUserService;

    private boolean isInForgetPasswordRequest() {
        RequestAttributes ras = RequestContextHolder.getRequestAttributes();
        if (null != ras && ras instanceof ServletRequestAttributes) {
            HttpServletRequest request = ((ServletRequestAttributes) ras).getRequest();
            String referer = request.getHeader("Referer");
            if (StringUtils.hasText(referer)) {
                return referer.endsWith(forgetPasswordPageUrl);
            }
        }
        return false;
    }

    private String getDataKey(String username, String phone) {
        return FORGET_PASSWORD_DATA_PREFIX + username + phone;
    }

    @Around("execution(* com.yuncheng.system.orguser.controller.SysUserController.querySysUser(..))")
    public Object doAroundQuerySysUser(ProceedingJoinPoint proceedingJoinPoint) {
        if (logger.isDebugEnabled()) {
            logger.debug("===环绕通知的目标方法名：" + proceedingJoinPoint.getSignature().getName());
        }
        try {
            //public HttpResult<Map<String, Object>> querySysUser(SysUser a)
            Object[] argsBefore = proceedingJoinPoint.getArgs();
            if (null != argsBefore && argsBefore.length == 1 && isInForgetPasswordRequest()) {
                SysUser su = (SysUser) argsBefore[0];
                String resultPhone = su.getPhone();
                if (StringUtils.hasText(resultPhone)) {
                    if (sysUserService.lambdaQuery().eq(SysUser::getPhone, resultPhone).list().size() > 1) {
                        return HttpResult.error("手机号绑定了多个账号，不能进行下一步操作。可以试试用账号来找回密码");
                    }
                }
            }

            HttpResult<Map<String, Object>> httpResult = (HttpResult<Map<String, Object>>) proceedingJoinPoint.proceed();
            if (null != httpResult && httpResult.isSuccess()) {
                Object[] args = proceedingJoinPoint.getArgs();
                if (null != args && args.length == 1 && isInForgetPasswordRequest()) {
                    SysUser su = (SysUser) args[0];
                    String resultUsername = (String) httpResult.getResult().get("username");
                    String resultPhone = (String) httpResult.getResult().get("phone");
                    boolean changed = false;
                    if (StringUtils.hasText(su.getUsername()) && su.getUsername().equals(resultUsername)) {
                        if (StringUtils.hasText(resultPhone)) {
                            int startInclude = 3;
                            int endExclude = resultPhone.length() - 2;
                            if (endExclude <= startInclude) {
                                endExclude = resultPhone.length();
                            }
                            resultPhone = StrUtil.hide(resultPhone, startInclude, endExclude);
                            changed = true;
                        }
                    } else if (StringUtils.hasText(su.getPhone()) && su.getPhone().equals(resultPhone)) {
                        if (StringUtils.hasText(resultUsername)) {
                            int startInclude = 2;
                            int endExclude = resultUsername.length() - 1;
                            if (endExclude <= startInclude) {
                                endExclude = resultUsername.length();
                            }
                            resultUsername = StrUtil.hide(resultUsername, startInclude, endExclude);
                            changed = true;
                        }
                    }
                    if (changed) {
                        String origData = JSONUtil.toJsonStr(httpResult.getResult());
                        httpResult.getResult().put("username", resultUsername);
                        httpResult.getResult().put("phone", resultPhone);
                        redisUtil.set(getDataKey(resultUsername, resultPhone), origData, 600);
                    }
                }
            }
            return httpResult;
        } catch (Throwable e) {
            logger.error("", e);
            if (e instanceof RuntimeException) {
                throw (RuntimeException) e;
            } else {
                throw new RuntimeException(e);
            }
        }

    }

    private void replaceArgs(Object[] args, String phoneKeyName) {
        JSONObject jsonObject = null;
        String mobile = null;
        String username = null;
        if (null != args && args.length == 1) {
            jsonObject = (JSONObject) args[0];
            if (null != jsonObject) {
                mobile = jsonObject.getString(phoneKeyName);
                username = jsonObject.getString("username");
            }
        }
        if (StringUtils.hasText(mobile) && (mobile.indexOf("*") >= 0) && isInForgetPasswordRequest()) {
            String phone = JSONUtil.parseObj(redisUtil.get(getDataKey(username, mobile))).getStr("phone");
            if (StringUtils.hasText(phone)) {
                jsonObject.put(phoneKeyName, phone);
            }
        }
        if (StringUtils.hasText(username) && (username.indexOf("*") >= 0) && isInForgetPasswordRequest()) {
            String savedUsername = JSONUtil.parseObj(redisUtil.get(getDataKey(username, mobile))).getStr("username");
            if (StringUtils.hasText(savedUsername)) {
                jsonObject.put("username", savedUsername);
            }
        }
    }

    @Around("execution(* com.yuncheng.system.login.controller.LoginController.sms(..))")
    public Object doAroundSms(ProceedingJoinPoint proceedingJoinPoint) {
        if (logger.isDebugEnabled()) {
            logger.debug("===环绕通知的目标方法名：" + proceedingJoinPoint.getSignature().getName());
        }
        try {
            //public HttpResult<?> sms(@RequestBody JSONObject jsonObject)
            replaceArgs(proceedingJoinPoint.getArgs(), "mobile");
            HttpResult<String> httpResult = (HttpResult<String>) proceedingJoinPoint.proceed();
            return httpResult;
        } catch (Throwable e) {
            logger.error("", e);
            if (e instanceof RuntimeException) {
                throw (RuntimeException) e;
            } else {
                throw new RuntimeException(e);
            }
        }
    }

    // 修正phoneVerification中验证码未设置超时时间的BUG
    @Around("execution(* com.yuncheng.system.orguser.controller.SysUserController.phoneVerification(..))")
    public Object doAroundPhoneVerification(ProceedingJoinPoint proceedingJoinPoint) {
        if (logger.isDebugEnabled()) {
            logger.debug("===环绕通知的目标方法名：" + proceedingJoinPoint.getSignature().getName());
        }
        try {
            //public HttpResult<String> phoneVerification(@RequestBody JSONObject jsonObject)
            replaceArgs(proceedingJoinPoint.getArgs(), "phone");
            HttpResult<String> httpResult = (HttpResult<String>) proceedingJoinPoint.proceed();
            if (null != httpResult && httpResult.isSuccess()) {
                Object[] args = proceedingJoinPoint.getArgs();
                if (null != args && args.length == 1) {
                    JSONObject jsonObject = (JSONObject) args[0];
                    if (null != jsonObject) {
                        String phone = jsonObject.getString("phone");
                        Object code = redisUtil.get(phone);
                        // 设置验证码超时时间10分钟，修正验证码不超时的BUG
                        logger.debug("===redisUtil.set(" + phone + ", " + code + ", 600)");
                        redisUtil.set(phone, code, 600);
                    }
                }
            }
            return httpResult;
        } catch (Throwable e) {
            logger.error("", e);
            if (e instanceof RuntimeException) {
                throw (RuntimeException) e;
            } else {
                throw new RuntimeException(e);
            }
        }
    }

    // 修正passwordChange修改密码成功后，未删除验证码的BUG
    @Around("execution(* com.yuncheng.system.orguser.controller.SysUserController.passwordChange(..))")
    public Object doAroundPasswordChange(ProceedingJoinPoint proceedingJoinPoint) {
        if (logger.isDebugEnabled()) {
            logger.debug("===环绕通知的目标方法名：" + proceedingJoinPoint.getSignature().getName());
        }
        try {
            //    public HttpResult<SysUser> passwordChange(@RequestParam(name = "username") String username,
            //                                              @RequestParam(name = "password") String password,
            //                                              @RequestParam(name = "smscode") String smscode,
            //                                              @RequestParam(name = "phone") String phone)

            int INDEX_USERNAME = 0;
            int INDEX_PHONE = 3;
            Object[] args = new Object[proceedingJoinPoint.getArgs().length];
            System.arraycopy(proceedingJoinPoint.getArgs(), 0, args, 0, args.length);
            String origusername = (String) args[INDEX_USERNAME];
            String origphone = (String) args[INDEX_PHONE];
            if (StringUtils.hasText(origusername) && StringUtils.hasText(origphone)
                    && (origusername.indexOf("*") >= 0 || origphone.indexOf("*") >= 0)) {
                JSONObject obj = new JSONObject();
                obj.put("username", origusername);
                obj.put("phone", origphone);
                Object[] objs = new Object[1];
                objs[0] = obj;
                replaceArgs(objs, "phone");
                args[INDEX_USERNAME] = obj.get("username");
                args[INDEX_PHONE] = obj.get("phone");
            }

            HttpResult<Object> httpResult = (HttpResult<Object>) proceedingJoinPoint.proceed(args);
            if (null != httpResult && httpResult.isSuccess()) {
                if (null != args && args.length == 4) {
                    String phone = (String) args[INDEX_PHONE];
                    // 密码修改成功，删除缓存中的验证码
                    if (null != phone) {
                        redisUtil.del(phone);
                        logger.debug("===redisUtil.del(" + phone + ")");
                    }
                }
            }
            return httpResult;
        } catch (Throwable e) {
            logger.error("", e);
            if (e instanceof RuntimeException) {
                throw (RuntimeException) e;
            } else {
                throw new RuntimeException(e);
            }
        }
    }
}
